DatabaseSequenceSelector.java
package org.codefilarete.stalactite.mapping.id.sequence;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collections;
import org.codefilarete.stalactite.sql.ConnectionProvider;
import org.codefilarete.stalactite.sql.ddl.structure.Sequence;
import org.codefilarete.stalactite.sql.statement.ReadOperation;
import org.codefilarete.stalactite.sql.statement.ReadOperationFactory;
import org.codefilarete.stalactite.sql.statement.StringParamedSQL;
import org.codefilarete.tool.VisibleForTesting;
import static org.codefilarete.tool.bean.Objects.preventNull;
/**
* Accessor for database sequence value.
*
* @author Guillaume Mary
*/
public class DatabaseSequenceSelector implements org.codefilarete.tool.function.Sequence<Long> {
private final Sequence databaseSequence;
private final int poolSize;
private final ReadOperationFactory readOperationFactory;
private final ConnectionProvider connectionProvider;
private final InternalState internalState = new InternalState();
private final StringParamedSQL sqlOrder;
public DatabaseSequenceSelector(Sequence databaseSequence, String selectStatement, ReadOperationFactory readOperationFactory, ConnectionProvider connectionProvider) {
this.databaseSequence = databaseSequence;
this.poolSize = preventNull(databaseSequence.getBatchSize(), 1);
this.readOperationFactory = readOperationFactory;
this.connectionProvider = connectionProvider;
this.sqlOrder = new StringParamedSQL(selectStatement, Collections.EMPTY_MAP);
}
public Sequence getDatabaseSequence() {
return databaseSequence;
}
@Override
public synchronized Long next() {
if (!internalState.initialized) {
internalState.currentValue = callDatabase();
internalState.initialized = true;
return internalState.currentValue;
} else if (internalState.currentValue % poolSize == 0) {
internalState.currentValue = callDatabase();
return internalState.currentValue;
} else {
return ++internalState.currentValue;
}
}
@VisibleForTesting
long callDatabase() {
try (ReadOperation<String> readOperation = readOperationFactory.createInstance(sqlOrder, connectionProvider, 1)) {
ResultSet rs = readOperation.execute();
rs.next();
// we use index access to read next value column to avoid keeping the column name in this class
// moreover, it has no interest here since we only retrieve one column
return rs.getLong(1);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
private static class InternalState {
private long currentValue;
private boolean initialized = false;
}
}